package udp

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"time"

	"gitee.com/acquisition_driver_development/magus_iot/magus/logs"

	hostlink "gitee.com/acquisition_driver_development/magus_iot/common_library/hostlink_0903"
)

type UdpMaster struct {
	DA1 uint16 //目标PLC节点号
	DA2 uint16 //目标PLC单元号
	SA1 uint16 //源节点号

	hostlink.Master
	Tid uint16
}

func (this *UdpMaster) Initialize(DA1, DA2, SA1 uint16) (err error) {
	this.DA1 = DA1
	this.DA2 = DA2
	this.SA1 = SA1
	this.Master.Initialize()
	return
}

func (this *UdpMaster) MakeRequest(group *hostlink.Group) (cmd []byte) {
	cmd = make([]byte, 18)
	cmd[0], cmd[1], cmd[2], cmd[3] = 0x80, 0x00, 0x02, 0x00
	cmd[4], cmd[5], cmd[6], cmd[7] = byte(this.DA1), byte(this.DA2), 0x00, byte(this.SA1)
	cmd[8], cmd[9], cmd[10], cmd[11] = 0x00, 0x00, 0x01, 0x01 //0101表示读
	word_code, err := strconv.ParseUint(group.WordCode, 16, 32)
	if err != nil {
		logs.Error(err)
		return
	}
	cmd[12] = byte(uint16(word_code))
	cmd[13] = byte(uint16(group.BeginAddress) >> 8)
	cmd[14] = byte(uint16(group.BeginAddress))
	if group.IfBite {
		cmd[15] = group.BeginBit
	}
	cmd[16] = byte(uint16(group.Size) >> 8)
	cmd[17] = byte(uint16(group.Size))

	return
}

func (this *UdpMaster) Write(conn *net.UDPConn, data []byte) (n int, err error) {
	n, err = conn.Write(data)
	logs.Debug("S: % 2X", data)
	return
}

func (this *UdpMaster) Read(conn *net.UDPConn, group *hostlink.Group) (buf []byte, err error) {
	var l int
	if group.IfBite {
		l = group.GetSize()
	} else {
		l = group.GetSize() * 2
	}
	data := make([]byte, 14+l)
	n, err := ReadTimeout(conn, data, 3000)
	if err != nil {
		return
	}
	logs.Debug("R: % 2X", data)

	if n != 14+l {
		err = errors.New("read udp data error")
		return
	}

	buf, err = this.CheckData(data)
	return
}

//报文格式: C0  00   02   00   02   00   00   03   00   00   01 01       00 00    03 01
//		  ICF RSV  VCT  DNA  DA1  DA2  SNA  SA1  SA2  SID (FINS命令)   (错误码)  数据
func (this *UdpMaster) CheckData(data []byte) (buf []byte, err error) {
	if data[1] != 0 || data[2] != 2 {
		err = errors.New("error RSV or VCT")
		return
	}
	if data[9] != 0 {
		err = errors.New("error SID")
		return
	}
	if data[13] != 0 {
		err = errors.New("error code: " + fmt.Sprintf("%2X", data[12:14]))
		return
	}
	return data[14:], err
}

/*
*
 */
func ReadTimeout(udpconn *net.UDPConn, data []byte, mstimeout int) (n int, err error) {
	err = udpconn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(mstimeout)))
	if err != nil {
		return
	}
	n, _, err = udpconn.ReadFromUDP(data)
	if err != nil {
		logs.Error(err)
		return n, err
	}
	return n, err
}
